from flask import Flask, request, jsonify, render_template, redirect, url_for, session, flash
from flask_cors import CORS
import pymysql
import os
from werkzeug.utils import secure_filename
import PyPDF2
import docx
import re

import re
from openai import OpenAI

app = Flask(__name__)
CORS(app)
app.secret_key = 'your_secret_key'
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

@app.route('/api/reset_resume_id', methods=['POST'])
def reset_resume_id():
    db = get_db()
    cursor = db.cursor()
    cursor.execute('SELECT COUNT(*) FROM resumes;')
    count = cursor.fetchone()[0]
    next_id = count + 1
    cursor.execute(f'ALTER TABLE resumes AUTO_INCREMENT = {next_id};')
    db.commit()
    db.close()
    return jsonify({'success': True, 'msg': f'简历ID已重置为{next_id}'})

def get_db():
    return pymysql.connect(host='localhost', user='szx', password='050928', port=3306, database='ypzl')



# AI模拟面试路由
@app.route('/ai_interview_chat/<int:resume_id>')
def ai_interview_chat(resume_id):
    print(f"[DEBUG] ai_interview_chat resume_id: {resume_id}")
    db = get_db()
    print(f"[DEBUG] DB connect: {db}")
    cursor = db.cursor()
    cursor.execute('SELECT id FROM resumes WHERE id=%s', (resume_id,))
    row = cursor.fetchone()
    print(f"[DEBUG] SQL result: {row}")
    db.close()
    if not row:
        print(f"[DEBUG] 404 triggered for resume_id: {resume_id}")
        return render_template('404.html', msg='未找到该简历'), 404
    print(f"[DEBUG] Render ai_interview_chat.html for resume_id: {resume_id}")
    return render_template('ai_interview_chat.html', resume_id=resume_id)


# 岗位离职风险预测API
@app.route('/api/position_risk', methods=['POST'])
def position_risk():
    try:
        data = request.get_json(force=True)
        resume_id = data.get('resume_id')
        position = data.get('position', '').strip()
        if not resume_id:
            return jsonify({'success': False, 'msg': '缺少resume_id参数'}), 400
        if not position:
            return jsonify({'success': False, 'msg': '缺少岗位名称参数'}), 400
        db = get_db()
        cursor = db.cursor()
        cursor.execute('SELECT content FROM resumes WHERE id=%s', (resume_id,))
        row = cursor.fetchone()
        db.close()
        if not row:
            return jsonify({'success': False, 'msg': '未找到简历内容'}), 404
        text = row[0] if row[0] else ''
        # 升级AI prompt，强制要求结合三大维度分析，输出结构化分值，提升稳定性
        prompt = f"""
你是一名专业HR，请严格按照以下要求分析候选人“{position}”岗位的离职风险，必须结合简历内容，逐条判断：
请忽略简历中的意向岗位字段，仅以输入的岗位“{position}”为分析对象。

【分析维度】
1. 职业稳定性（重点看工作时长、离职频率、空白期、调岗频率等）
2. 求职动机一致性（行业/岗位/公司跨度，目标与经历是否匹配）
3. 职业规划清晰度（项目/成果积累，目标明确性，技能提升方向等）

【输出格式】
- 岗位名称：xxx
- 职业稳定性分值：0.x（0-1，1为高风险，需结合简历具体表现打分）
- 求职动机一致性分值：0.x
- 职业规划清晰度分值：0.x
- 综合离职风险分值：0.x（请综合前三项，给出最终风险分值，0为极低，1为极高）
- 详细分析：分点说明每一项的理由，引用简历中的具体内容，避免泛泛而谈。

【注意】
- 必须严格按上述格式输出，分值必须为0-1之间的小数，且每一项都要有理由。
- 不要只复述简历内容，要结合岗位需求做针对性分析。
"""
        prompt += f"\n简历内容：{text}"
        response = client.chat.completions.create(
            model="deepseek-chat",
            messages=[
                {"role": "system", "content": "你是一个专业的HR，善于分析岗位离职风险。"},
                {"role": "user", "content": prompt},
            ],
            stream=False,
            temperature=0.2
        )
        ai_content = response.choices[0].message.content.strip()
        # 优先提取结构化分值，提升稳定性
        def extract_score(label, text):
            m = re.search(rf'{label}[：: ]*([01](?:\.\d+)?)', text)
            if m:
                try:
                    return float(m.group(1))
                except Exception:
                    return None
            return None
        stable = extract_score('职业稳定性分值', ai_content)
        motive = extract_score('求职动机一致性分值', ai_content)
        plan = extract_score('职业规划清晰度分值', ai_content)
        risk = extract_score('综合离职风险分值', ai_content)
        # 若综合分值缺失，自动加权平均三项
        if risk is None:
            vals = [v for v in [stable, motive, plan] if v is not None]
            if vals:
                risk = round(sum(vals)/len(vals), 2)
        # 若仍无，回退到第一个0-1小数
        if risk is None:
            risk_match = re.search(r'(0(\.\d+)?|1(\.0+)?)', ai_content)
            risk = float(risk_match.group(0)) if risk_match else None
        if risk is None or not (0 <= risk <= 1):
            risk = 0.5  # 默认中等风险
        return jsonify({'success': True, 'risk': risk, 'msg': ai_content})
    except Exception as e:
        return jsonify({'success': False, 'msg': f'后端异常: {e}'})

client = OpenAI(api_key="sk-58edacbfb5cf42acb8ed3703b13a9ca6", base_url="https://api.deepseek.com")

# 简历关键信息解析函数
def parse_resume_info(text):
    job_match = re.search(r'(意向岗位|岗位|职位|应聘岗位)[：:\s]*([^\s，。；\n\r]+)', text)
    job = job_match.group(2) if job_match else ''
    name_match = re.search(r'(姓名)[：:\s]*([^\s，。；\n\r]+)', text)
    name = name_match.group(2) if name_match else ''
    edu_match = re.search(r'(博士|硕士|本科|大专|高中|中专|本科在读)', text)
    education = edu_match.group(1) if edu_match else ''
    exp_match = re.search(r'(\d{1,2}年|应届生|1-3年|3-5年|5年以上)', text)
    experience = exp_match.group(1) if exp_match else ''
    loc_match = re.search(r'(工作地点|现居住地|期望城市|期望地点|城市)[：:\s]*([^\s，。；\n\r]+)', text)
    location = loc_match.group(2) if loc_match else ''
    salary_match = re.search(r'(期望薪资|薪资|期望工资|期望月薪)[：:\s]*([0-9\-~kK万]+)', text)
    salary = salary_match.group(2) if salary_match else ''
    return {
        'job': job,
        'name': name,
        'education': education,
        'experience': experience,
        'location': location,
        'salary': salary
    }

# AI简历分析函数
def analyze_resume_with_ai(text):
    prompt = f"请帮我分析如下简历内容，提取姓名、意向岗位、学历、技能、工作经验、期望薪资、年龄等关键信息，返回结构化结果：\n{text}"
    response = client.chat.completions.create(
        model="deepseek-chat",
        messages=[
            {"role": "system", "content": "你是一个专业的简历解析助手，输出结构化JSON。"},
            {"role": "user", "content": prompt},
        ],
        stream=False
    )
    return response.choices[0].message.content

@app.route('/api/recommended', methods=['GET'])
def recommended_candidates():
    try:
        query = request.args.get('query', '').strip()
        db = get_db()
        cursor = db.cursor()
        sql = 'SELECT id, filename, content, ai_content, upload_time FROM resumes'
        params = []
        cursor.execute(sql, params)
        candidates = []
        for r in cursor.fetchall():
            if not isinstance(r, (list, tuple)) or len(r) < 4:
                continue
            if len(r) >= 5:
                id, filename, content, job, upload_time = r[:5]
            else:
                id, filename, content, upload_time = r[:4]
                job = ''
            text = str(content)
            import json
            ai_related = False
            ai_score = 0
            ai_json = None
            ai_reason = ''
            if query:
                # 让AI判断该简历与关键词的能力匹配度，返回0-100分
                ai_prompt = f"请你作为专业HR，分析如下简历内容与‘{query}’的能力匹配度，满分100分，越相关分数越高。请严格输出如下格式：\n匹配度分数：X\n理由：...\n简历内容：\n{text}"
                try:
                    ai_resp = client.chat.completions.create(
                        model="deepseek-chat",
                        messages=[
                            {"role": "system", "content": "你是一个专业的简历分析助手。"},
                            {"role": "user", "content": ai_prompt},
                        ],
                        stream=False
                    )
                    ai_content = ai_resp.choices[0].message.content.strip()
                    m = re.search(r'匹配度分数[：: ]*(\d{1,3})', ai_content)
                    if m:
                        ai_score = min(max(int(m.group(1)), 0), 100)
                        ai_related = ai_score > 0
                    else:
                        ai_related = False
                    ai_reason = ai_content
                except Exception as e:
                    ai_related = False
                    ai_reason = f"AI分析异常: {e}"
            # 字段提取
            job_match = re.search(r'(意向岗位|岗位|职位|应聘岗位)[：: ]*([\u4e00-\u9fa5A-Za-z0-9\-_/（）()]+)', text)
            if job_match:
                job = job_match.group(2).strip()
            else:
                job_match2 = re.search(r'(UI|UX)[/\\]?([\u4e00-\u9fa5A-Za-z ]+设计师)', text, re.IGNORECASE)
                job = job_match2.group(0).strip() if job_match2 else ''
                if not job:
                    job_match3 = re.search(r'(岗位|职位|意向)[：: ]*([\u4e00-\u9fa5A-Za-z0-9\-_/（）() ]+设计师)', text)
                    job = job_match3.group(2).strip() if job_match3 else ''
            name_match = re.search(r'(姓名)[：: ]*([\u4e00-\u9fa5A-Za-z]+)', text)
            name = name_match.group(2).strip() if name_match else ''
            edu_match = re.findall(r'(博士|硕士|本科|大专|高中|中专|本科在读)', text)
            education = edu_match[0] if edu_match else ''
            # 智能提取工作年数（避免误提年份）
            experience = ''
            exp_patterns = [
                r'工作经验[：: ]*(\d{1,2})年',
                r'(\d{1,2})年工作经验',
                r'经验[：: ]*(\d{1,2})年',
                r'(\d{1,2})年以上',
                r'(\d{1,2})\s*years?',
            ]
            for pat in exp_patterns:
                m = re.search(pat, text, re.IGNORECASE)
                if m:
                    num = m.group(1)
                    if num.isdigit():
                        experience = f"{num}年" if '年' in pat else f"{num} years"
                        break
            if not experience:
                m = re.search(r'(\d{1,2})[\-~—](\d{1,2})年', text)
                if m:
                    n1, n2 = m.group(1), m.group(2)
                    try:
                        n1, n2 = int(n1), int(n2)
                        experience = f"{max(n1, n2)}年"
                    except:
                        pass
            if not experience:
                m = re.search(r'(20\d{2})[\-~—](20\d{2})', text)
                if m:
                    y1, y2 = int(m.group(1)), int(m.group(2))
                    if y2 >= y1 and y2 <= 2100:
                        years = y2 - y1
                        if years > 0:
                            experience = f"{years}年"
            if not experience:
                m = re.search(r'应届生', text)
                if m:
                    experience = '应届生'
            if not experience:
                exp_match = re.search(r'(\d{1,2}年|应届生|1-3年|3-5年|5年以上)', text)
                if exp_match:
                    before = text[max(0, exp_match.start()-4):exp_match.start()]
                    if not re.match(r'20\d{2}', before):
                        experience = exp_match.group(1)
            salary_match = re.search(r'(期望薪资|薪资|期望工资|期望月薪)[：: ]*([\d\w\-~kK]+)', text)
            salary = salary_match.group(2).strip() if salary_match else ''
            job_simple = job.strip().replace('（', '').replace('）', '').replace('(', '').replace(')', '')[:10]
            age = ''
            age_match = re.search(r'年龄[：: ]*(\d{1,2})岁', text)
            if age_match:
                age = age_match.group(1)
            else:
                age2 = re.search(r'年龄[：: ]*(\d{1,2})', text)
                if age2:
                    age = age2.group(1)
                else:
                    age3 = re.search(r'(\d{1,2})岁', text)
                    age = age3.group(1) if age3 else ''
            match_score = 0
            match_points = []
            query_lower = query.lower() if query else ''
            # 字段/AI双重检索
            match_hit = False
            fields_to_match = [job_simple, job, name, education, experience, salary, text]
            for field in fields_to_match:
                if field and (query_lower in str(field).lower()):
                    match_hit = True
                    match_points.append(f"关键词相关：{field}")
                    break
            # AI相关性命中
            if ai_related:
                match_hit = True
                match_points.append(f"AI能力匹配度：{ai_score}分")
                match_score = ai_score
                if ai_reason:
                    match_points.append(ai_reason)
            # 若字段命中但AI未命中，给基础分
            if match_hit and not ai_related:
                match_score = 30
            if not query:
                # 首页无搜索时，所有简历都展示，匹配度给基础分
                match_score = 60
                match_points = ["默认推荐"]
                candidates.append({
                    'id': id,
                    'filename': filename,
                    'job': job_simple,
                    'name': name,
                    'age': age,
                    'education': education,
                    'experience': experience,
                    'salary': salary,
                    'upload_time': str(upload_time),
                    'match_score': match_score,
                    'match_points': match_points
                })
            elif match_hit:
                candidates.append({
                    'id': id,
                    'filename': filename,
                    'job': job_simple,
                    'name': name,
                    'age': age,
                    'education': education,
                    'experience': experience,
                    'salary': salary,
                    'upload_time': str(upload_time),
                    'match_score': match_score,
                    'match_points': match_points
                })
        return jsonify({'success': True, 'candidates': candidates})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})
        match_reason = "未检测到岗位关键词，"
    common_skills = ['Python', 'Java', 'JavaScript', 'SQL', '项目管理', '沟通能力', '团队合作', 'Excel', 'PPT', '数据分析', '沟通', '领导力', '创新']
    skill_matches = [skill for skill in common_skills if skill.lower() in text.lower()]
    skill_score = min(len(skill_matches) * 2, 12)
    skill_reason = f"匹配通用技能{len(skill_matches)}项：{', '.join(skill_matches)}"
    score_details['岗位匹配度']['score'] = min(match_score + skill_score, 30)
    score_details['岗位匹配度']['reason'] = match_reason + skill_reason
    richness_score = 0
    richness_issues = []
    text_length = len(text)
    if text_length > 1500:
        richness_score += 10
    elif text_length > 900:
        richness_score += 7
    elif text_length > 500:
        richness_score += 5
    else:
        richness_issues.append("内容过于简短")
    achievement_patterns = [r'\d+[年月天]', r'完成', r'实现', r'提升', r'增长', r'负责', r'项目', r'成果', r'奖项', r'荣誉']
    achievements = sum(1 for pattern in achievement_patterns if re.search(pattern, text))
    if achievements >= 4:
        richness_score += 10
    elif achievements >= 2:
        richness_score += 7
    elif achievements >= 1:
        richness_score += 3
    else:
        richness_issues.append("缺少具体项目或成果描述")
    score_details['内容丰富度']['score'] = min(richness_score, 20)
    if richness_issues:
        score_details['内容丰富度']['reason'] = "内容方面: " + "; ".join(richness_issues)
    else:
        score_details['内容丰富度']['reason'] = "内容详实，项目与成果描述丰富"
    highlight_score = 0
    highlight_issues = []
    if re.search(r'[★▶■●▲◆]{2,}|[#*]{2,}', text):
        highlight_score += 8
    else:
        highlight_issues.append("缺少重点符号或排版标记")
    if re.search(r'\d+(%|年|月|人|万|k|次|项|分)', text):
        highlight_score += 7
    else:
        highlight_issues.append("缺少量化数据（数字、百分比、时间等）")
    score_details['关键信息突出度']['score'] = highlight_score
    if highlight_issues:
        score_details['关键信息突出度']['reason'] = "突出性方面: " + "; ".join(highlight_issues)
    else:
        score_details['关键信息突出度']['reason'] = "关键信息突出，符号与数据标记明显"
    total_score = sum(score_details[dim]['score'] for dim in score_details if dim != '总分')
    score_details['总分'] = total_score
    result = {"score": score_details, "ai_summary": ai_summary}
    print("后端评分返回：", result)
    return result

# 简历关键信息解析函数
def parse_resume_info(text):
    job_match = re.search(r'(意向岗位|岗位|职位|应聘岗位)[：:\s]*([^\s，。；\n\r]+)', text)
    job = job_match.group(2) if job_match else ''
    name_match = re.search(r'(姓名)[：:\s]*([^\s，。；\n\r]+)', text)
    name = name_match.group(2) if name_match else ''
    edu_match = re.search(r'(博士|硕士|本科|大专|高中|中专|本科在读)', text)
    education = edu_match.group(1) if edu_match else ''
    exp_match = re.search(r'(\d{1,2}年|应届生|1-3年|3-5年|5年以上)', text)
    experience = exp_match.group(1) if exp_match else ''
    loc_match = re.search(r'(工作地点|现居住地|期望城市|期望地点|城市)[：:\s]*([^\s，。；\n\r]+)', text)
    location = loc_match.group(2) if loc_match else ''
    salary_match = re.search(r'(期望薪资|薪资|期望工资|期望月薪)[：:\s]*([0-9\-~kK万]+)', text)
    salary = salary_match.group(2) if salary_match else ''
    return {
        'job': job,
        'name': name,
        'education': education,
        'experience': experience,
        'location': location,
        'salary': salary
    }

# 简历评分API
CORS(app)
app.secret_key = 'your_secret_key'

# 上传文件夹配置
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

# MySQL数据库连接函数
def get_db():
    db_config = {
        'host': 'localhost',
        'user': 'szx',
        'password': '050928',
        'database': 'ypzl',
        'port': 3306,
        'charset': 'utf8'
    }
    try:
        db = pymysql.connect(**db_config)
        # 测试连接
        cursor = db.cursor()
        cursor.execute('SELECT 1')
        print('数据库连接成功')
        return db
    except Exception as e:
        print(f'数据库连接失败: {e}')
        raise
# 简历质量评分函数
def score_resume(text, job_keyword):
    # 简单评分逻辑，可根据实际需求扩展
    score_details = {
        '岗位匹配度': {'score': 0, 'reason': ''},
        '内容丰富度': {'score': 0, 'reason': ''},
        '关键信息突出度': {'score': 0, 'reason': ''},
        '总分': 0
    }
    # 岗位匹配度
    if job_keyword and job_keyword in text:
        score_details['岗位匹配度']['score'] = 30
        score_details['岗位匹配度']['reason'] = f"检测到岗位关键词：{job_keyword}"
    else:
        score_details['岗位匹配度']['reason'] = "未检测到岗位关键词"
    # 内容丰富度
    text_length = len(text)
    if text_length > 1500:
        score_details['内容丰富度']['score'] = 20
        score_details['内容丰富度']['reason'] = "内容详实"
    elif text_length > 900:
        score_details['内容丰富度']['score'] = 15
        score_details['内容丰富度']['reason'] = "内容较丰富"
    elif text_length > 500:
        score_details['内容丰富度']['score'] = 10
        score_details['内容丰富度']['reason'] = "内容一般"
    else:
        score_details['内容丰富度']['score'] = 5
        score_details['内容丰富度']['reason'] = "内容较少"
    # 关键信息突出度
    if re.search(r'[★▶■●▲◆#*]{2,}', text):
        score_details['关键信息突出度']['score'] = 10
        score_details['关键信息突出度']['reason'] = "关键信息突出"
    else:
        score_details['关键信息突出度']['score'] = 5
        score_details['关键信息突出度']['reason'] = "关键信息一般"
    # 总分
    score_details['总分'] = sum(score_details[dim]['score'] for dim in score_details if dim != '总分')
    # AI分析总结
    ai_summary = ""
    # 若内容较长，自动调用AI分析
    if len(text) > 100:
        try:
            prompt = f"请根据以下简历内容，简要总结该候选人的优势、技能、劣势和推荐岗位，要求分点列出，突出亮点和不足：\n{text}"
            response = client.chat.completions.create(
                model="deepseek-chat",
                messages=[
                    {"role": "system", "content": "你是一个专业的HR，善于总结简历亮点和不足。"},
                    {"role": "user", "content": prompt},
                ],
                stream=False
            )
            ai_summary = response.choices[0].message.content.strip()
        except Exception as e:
            ai_summary = "AI分析异常：" + str(e)
    return {"score": score_details, "ai_summary": ai_summary}
    # 使用 parse_resume_info 或自定义评分函数
    score_result = score_resume(text, job_keyword)
# 简历关键信息解析函数
def parse_resume_info(text):
    job_match = re.search(r'(意向岗位|岗位|职位|应聘岗位)[：:\s]*([^\s，。；\n\r]+)', text)
    job = job_match.group(2) if job_match else ''
    name_match = re.search(r'(姓名)[：:\s]*([^\s，。；\n\r]+)', text)
    name = name_match.group(2) if name_match else ''
    edu_match = re.search(r'(博士|硕士|本科|大专|高中|中专|本科在读)', text)
    education = edu_match.group(1) if edu_match else ''
    exp_match = re.search(r'(\d{1,2}年|应届生|1-3年|3-5年|5年以上)', text)
    experience = exp_match.group(1) if exp_match else ''
    loc_match = re.search(r'(工作地点|现居住地|期望城市|期望地点|城市)[：:\s]*([^\s，。；\n\r]+)', text)
    location = loc_match.group(2) if loc_match else ''
    salary_match = re.search(r'(期望薪资|薪资|期望工资|期望月薪)[：:\s]*([0-9\-~kK万]+)', text)
    salary = salary_match.group(2) if salary_match else ''
    return {
        'job': job,
        'name': name,
        'education': education,
        'experience': experience,
        'location': location,
        'salary': salary
    }

# 简历评分API
@app.route('/api/score_resume')
def api_score_resume():
    resume_id = request.args.get('id', type=int)
    db = get_db()
    cursor = db.cursor()
    cursor.execute('SELECT content FROM resumes WHERE id=%s', (resume_id,))
    row = cursor.fetchone()
    db.close()
    if not row:
        return jsonify({'success': False, 'error': '未找到简历内容'})
    text = row[0] if row[0] else ''
    # 只返回AI优势/技能/劣势/推荐岗位总结
    ai_summary = None
    if hasattr(client, 'chat'):
        prompt = f"请根据如下简历内容，提炼AI优势、技能、劣势、推荐岗位总结，输出结构化结果：\n{text}"
        response = client.chat.completions.create(
            model="deepseek-chat",
            messages=[
                {"role": "system", "content": "你是一个专业的简历解析助手，只输出AI优势/技能/劣势/推荐岗位总结。"},
                {"role": "user", "content": prompt},
            ],
            stream=False
        )
        ai_summary = response.choices[0].message.content if response.choices else None
    return jsonify({'success': True, 'score': {'ai_summary': ai_summary}})
    db_config = {
        'host': 'localhost',
        'user': 'root',
        'password': '123456',
        'database': 'ypzl',
        'port': 3308,
        'charset': 'utf8'
    }
    return pymysql.connect(**db_config)

# 自动建表（简历表）
def init_resume_table():
    db = get_db()
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS resumes (
        id INT AUTO_INCREMENT PRIMARY KEY,
        filename VARCHAR(255),
        content LONGTEXT,
        ai_content LONGTEXT,
        upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )''')
    db.commit()
    db.close()
init_resume_table()

# 自动建表（用户表）
def init_user_table():
    db = get_db()
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        username VARCHAR(50) UNIQUE NOT NULL,
        password VARCHAR(100) NOT NULL
    )''')
    db.commit()
    db.close()
init_user_table()

# 工具函数
ALLOWED_EXTENSIONS = {'pdf', 'doc', 'docx', 'txt'}
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

# 路由区域


# 按 uploads 文件夹文件数重置 resumes 表自增主键
@app.route('/api/refresh_resume_id', methods=['POST'])
def refresh_resume_id():
    try:
        uploads_dir = os.path.join(os.getcwd(), 'uploads')
        if not os.path.exists(uploads_dir):
            return jsonify({'success': False, 'msg': 'uploads文件夹不存在'})
        file_count = len([f for f in os.listdir(uploads_dir) if os.path.isfile(os.path.join(uploads_dir, f))])
        new_auto_increment = file_count + 1
        db = get_db()
        cursor = db.cursor()
        cursor.execute(f'ALTER TABLE resumes AUTO_INCREMENT = {new_auto_increment}')
        db.commit()
        cursor.close()
        db.close()
        return jsonify({'success': True, 'msg': f'resumes表自增主键已重置为{new_auto_increment}（uploads文件数={file_count}）'})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})
@app.route('/api/insert_test_resume', methods=['POST'])
def insert_test_resume():
    db = get_db()
    cursor = db.cursor()
    # 构造测试简历内容，包含常用字段
    filename = '测试简历_张三.txt'
    content = '''姓名：张三\n意向岗位：前端开发\n学历：本科\n工作经验：3年\n期望薪资：12k-15k\n技能：JavaScript, Vue, React, HTML, CSS\n'''
    cursor.execute('SELECT MAX(id) FROM resumes')
    max_id = cursor.fetchone()[0] or 0
    new_id = max_id + 1
    cursor.execute('INSERT INTO resumes (id, filename, content, upload_time) VALUES (%s, %s, %s, NOW())', (new_id, filename, content))
    db.commit()
    cursor.close()
    db.close()
    return jsonify({'success': True, 'msg': f'测试简历已插入，id={new_id}'})
# 首页
@app.route('/')
def index():
    if 'username' not in session:
        flash('请先登录！')
        return redirect(url_for('login'))
    return render_template('homepage2.html')

# 简历上传
@app.route('/upload_resume', methods=['GET', 'POST'])
def upload_resume():
    if request.method == 'POST':
        file = request.files.get('resume')
        if file and allowed_file(file.filename):
            filename = file.filename.strip()
            # 允许中文文件名，只校验后缀
            if not filename or '.' not in filename:
                flash('文件名无后缀或格式错误，请重新上传！')
                return redirect(url_for('upload_resume'))
            filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
            file.save(filepath)
            ext = filename.rsplit('.', 1)[1].lower()
            text = ''
            try:
                if ext == 'txt':
                    with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                        text = f.read()
                elif ext == 'pdf':
                    try:
                        import pdfplumber
                        with pdfplumber.open(filepath) as pdf:
                            for page in pdf.pages:
                                text += page.extract_text() or ''
                    except Exception as e:
                        try:
                            with open(filepath, 'rb') as f:
                                reader = PyPDF2.PdfReader(f)
                                for page in reader.pages:
                                    text += page.extract_text() or ''
                        except Exception as e2:
                            text = f'PDF解析失败: pdfplumber异常={e}, PyPDF2异常={e2}'
                elif ext == 'doc':
                    text = '暂不支持 .doc 格式，请上传 docx/pdf/txt 格式简历。'
                elif ext == 'docx':
                    doc = docx.Document(filepath)
                    for para in doc.paragraphs:
                        text += para.text + '\n'
                    # 处理表格
                    for table in doc.tables:
                        html_table = '<table style="width:100%;margin:18px 0;background:#fff;border-radius:8px;box-shadow:0 2px 8px rgba(0,113,227,0.05);border:1px solid #e0e0e0;">'
                        for row in table.rows:
                            html_table += '<tr>'
                            for cell in row.cells:
                                html_table += f'<td style="padding:10px;color:#0071e3;">{cell.text}</td>'
                            html_table += '</tr>'
                        html_table += '</table>'
                        text += '[表格]' + html_table + '\n'
                    # 处理图片
                    for rel in doc.part.rels.values():
                        if "image" in rel.target_ref:
                            img_url = rel.target_ref  # 这里只能存图片路径或链接，实际部署需处理图片保存和访问
                            text += f'[图片]{img_url}\n'
                else:
                    text = '暂不支持该文件类型解析。'
            except Exception as e:
                text = f'文件解析失败: {e}'
            info = parse_resume_info(text)
            job = info.get('job', '')
            ai_result = ''
            conn = get_db()
            cur = conn.cursor()
            try:
                cur.execute("SELECT job FROM resumes LIMIT 1")
                has_job = True
            except Exception:
                has_job = False
            if has_job:
                cur.execute('INSERT INTO resumes (filename, content, ai_content, job, upload_time) VALUES (%s, %s, %s, %s, NOW())', (filename, text, ai_result, job))
            else:
                cur.execute('INSERT INTO resumes (filename, content, ai_content, upload_time) VALUES (%s, %s, %s, NOW())', (filename, text, ai_result))
            conn.commit()
            new_id = cur.lastrowid
            cur.close()
            conn.close()
            session['new_resume_id'] = new_id
            flash(f'简历上传成功！AI分析结果已写入数据库。')
            return redirect(url_for('upload_resume'))
        else:
            flash('文件类型不支持！')
            return redirect(url_for('upload_resume'))
    new_id = session.get('new_resume_id', None)
    if new_id:
        session.pop('new_resume_id', None)
    return render_template('upload_resume.html', new_id=new_id)

# 简历列表页面
@app.route('/resumes')
def resume_list():
    db = get_db()
    cursor = db.cursor()
    cursor.execute('SELECT id, filename, upload_time, content FROM resumes ORDER BY id DESC')
    resumes = []
    for r in cursor.fetchall():
        if not isinstance(r, (list, tuple)) or len(r) < 4:
            continue
        # 兼容数据库有job字段的情况
        if len(r) >= 5:
            id, filename, content, job, upload_time = r[:5]
        else:
            id, filename, upload_time, content = r[:4]
            job = ''
        resumes.append((id, filename, upload_time))
    db.close()
    # 判断请求类型，API返回JSON，页面渲染返回模板
    if request.headers.get('Accept', '').startswith('application/json') or request.args.get('api') == '1':
        # 返回JSON格式
        return jsonify({'success': True, 'resumes': [ {'id': r[0], 'filename': r[1], 'upload_time': str(r[2])} for r in resumes ] })
    # 页面渲染

    return render_template('resume_list.html', resumes=resumes)

# 删除简历路由（放在 resume_list 路由之后）
@app.route('/delete_resume', methods=['POST'])
def delete_resume():
    resume_id = request.form.get('resume_id')
    if not resume_id:
        flash('参数错误，未指定简历ID')
        return redirect(url_for('resume_list'))
    db = get_db()
    cursor = db.cursor()
    try:
        cursor.execute('DELETE FROM resumes WHERE id=%s', (resume_id,))
        db.commit()
        flash('简历已成功删除！')
    except Exception as e:
        flash(f'删除失败：{e}')
    finally:
        cursor.close()
        db.close()
    return redirect(url_for('resume_list'))

# 简历详情页面
@app.route('/resume/<int:resume_id>')
def resume_detail(resume_id):
    db = get_db()
    cursor = db.cursor()
    cursor.execute('SELECT id, filename, upload_time, content FROM resumes WHERE id=%s', (resume_id,))
    row = cursor.fetchone()
    db.close()
    if not row or len(row) < 4:
        flash('未找到该简历或数据异常')
        return redirect(url_for('resume_list'))
    # 保证字段顺序和类型
    id, filename, upload_time, content = row[:4]
    if content is None:
        content = ''
    elif not isinstance(content, str):
        try:
            content = str(content)
        except Exception:
            content = ''
    # 提取工作经验（与推荐API一致）
    exp_patterns = [
        r'工作经验[：: ]*(\d{1,2})年',
        r'(\d{1,2})年工作经验',
        r'经验[：: ]*(\d{1,2})年',
        r'(\d{1,2})年',
    ]
    experience = ''
    for pat in exp_patterns:
        m = re.search(pat, content)
        if m:
            experience = m.group(1) + '年' if m.lastindex else m.group(0)
            break
    if not experience:
        exp2 = re.search(r'应届生', content)
        if exp2:
            experience = exp2.group(0)
        else:
            exp3 = re.search(r'经验[：: ]*([\u4e00-\u9fa5A-Za-z0-9]+)', content)
            experience = exp3.group(1) if exp3 else ''
    resume = (id, filename, upload_time, content, experience)
    return render_template('resume_detail.html', resume=resume)

    # AI对话式面试路由，主模块级注册，确保无缩进和结构问题
    @app.route('/ai_interview_chat/<int:resume_id>')
    def ai_interview_chat(resume_id):
        print(f"[DEBUG] ai_interview_chat resume_id: {resume_id}")
        db = get_db()
        print(f"[DEBUG] DB connect: {db}")
        cursor = db.cursor()
        cursor.execute('SELECT id FROM resumes WHERE id=%s', (resume_id,))
        row = cursor.fetchone()
        print(f"[DEBUG] SQL result: {row}")
        db.close()
        if not row:
            print(f"[DEBUG] 404 triggered for resume_id: {resume_id}")
            return render_template('404.html', msg='未找到该简历'), 404
        print(f"[DEBUG] Render ai_interview_chat.html for resume_id: {resume_id}")
        return render_template('ai_interview_chat.html', resume_id=resume_id)

# 用户相关
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        cursor = db.cursor()
        cursor.execute("SELECT password FROM users WHERE username=%s", (username,))
        result = cursor.fetchone()
        db.close()
        if result and result[0] == password:
            session['username'] = username
            flash('登录成功！')
            return redirect(url_for('index'))
        else:
            flash('用户名或密码错误')
    return render_template('login.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        cursor = db.cursor()
        cursor.execute("SELECT id FROM users WHERE username=%s", (username,))
        if cursor.fetchone():
            flash('用户名已存在')
        else:
            cursor.execute("INSERT INTO users (username, password) VALUES (%s, %s)", (username, password))
            db.commit()
            session['username'] = username
            flash('注册成功！')
            db.close()
            return redirect(url_for('index'))
        db.close()
    return render_template('register.html')

@app.route('/logout')
def logout():
    session.pop('username', None)
    flash('已退出登录')
    return redirect(url_for('index'))

# 候选人相关API（原有功能保留）
candidateDatabase = []

@app.route('/api/candidates', methods=['GET'])
def search_candidates():
    # 获取查询参数
    query = request.args.get('query', '').lower()
    # 仅保留关键词搜索
    results = candidateDatabase
    if query:
        results = [candidate for candidate in results if (
            query in candidate['title'].lower() or
            query in candidate['name'].lower() or
            query in candidate['company'].lower() or
            any(query in skill['value'].lower() for skill in candidate['skills'])
        )]
    return jsonify({
        "success": True,
        "count": len(results),
        "candidates": results
    })

@app.route('/api/candidates/<int:candidate_id>', methods=['GET'])
def get_candidate(candidate_id):
    candidate = next((c for c in candidateDatabase if c['id'] == candidate_id), None)
    if candidate:
        return jsonify({
            "success": True,
            "candidate": candidate
        })
    else:
        return jsonify({
            "success": False,
            "message": "候选人未找到"
        }), 404

    db.commit()
    db.close()


# 简单用户数据库（内存，实际项目建议用数据库）
user_db = {}





# 简单用户数据库（内存，实际项目建议用数据库）
user_db = {}

# 使用前端提供的候选人数据库替换原有的产品数据库














@app.route('/api/interview_chat', methods=['GET', 'POST'])
def interview_chat_api():
    try:
        max_questions = 6  # 支持5~7个问题，默认6
        if request.method == 'GET':
            resume_id = request.args.get('resume_id')
            db = get_db()
            cursor = db.cursor()
            cursor.execute('SELECT content FROM resumes WHERE id=%s', (resume_id,))
            row = cursor.fetchone()
            db.close()
            if not row:
                print(f"[ERROR] 未找到简历内容 resume_id={resume_id}")
                return jsonify({'success': False, 'msg': '未找到简历内容'})
            text = row[0]
            print(f"[DEBUG] 简历内容: {text[:100]}")
            try:
                interview_prompt = f"根据以下简历内容，为该候选人生成1个有针对性的面试问题，突出意向岗位、技能或经验：\n{text}"
                interview_response = client.chat.completions.create(
                    model="deepseek-chat",
                    messages=[
                        {"role": "system", "content": "你是一个专业的HR面试官，能根据简历内容生成有针对性的面试问题。"},
                        {"role": "user", "content": interview_prompt},
                    ],
                    stream=False
                )
                question = interview_response.choices[0].message.content.strip()
                print(f"[DEBUG] AI问题: {question}")
                return jsonify({'success': True, 'question': question, 'max_questions': max_questions})
            except Exception as e:
                print(f"[ERROR] AI生成问题异常: {e}")
                return jsonify({'success': False, 'msg': f'AI生成问题异常: {e}'})
        else:
            data = request.get_json()
            resume_id = data.get('resume_id')
            question_idx = data.get('question_idx', 0)
            answer = data.get('answer', '')
            history = data.get('history', [])
            db = get_db()
            cursor = db.cursor()
            cursor.execute('SELECT content FROM resumes WHERE id=%s', (resume_id,))
            row = cursor.fetchone()
            db.close()
            text = row[0] if row else ''
            print(f"[DEBUG] 简历内容: {text[:100]}")
            try:
                analysis_prompt = f"面试问题：{history[-1]['q'] if history else ''}\n求职者答案：{answer}\n请你从专业能力、表达沟通、岗位匹配度、亮点与不足等多维度简要点评本题表现。"
                analysis_response = client.chat.completions.create(
                    model="deepseek-chat",
                    messages=[
                        {"role": "system", "content": "你是一个专业的HR面试官，能多维度点评求职者每题表现。"},
                        {"role": "user", "content": analysis_prompt},
                    ],
                    stream=False
                )
                analysis = analysis_response.choices[0].message.content.strip()
                next_idx = question_idx + 1
                finished = next_idx >= max_questions
                if finished:
                    report_prompt = f"以下是求职者的简历内容：\n{text}\n以下是面试问答历史：\n" + '\n'.join([f"Q:{h.get('q','')}\nA:{h.get('a','')}\n点评:{h.get('analysis','')}" for h in history]) + f"\n请你综合分析求职者整体表现，生成详细面试报告，包括专业能力、沟通表达、岗位匹配度、优缺点、录用建议等。"
                    report_response = client.chat.completions.create(
                        model="deepseek-chat",
                        messages=[
                            {"role": "system", "content": "你是一个专业的HR面试官，能综合所有问答生成详细面试报告。"},
                            {"role": "user", "content": report_prompt},
                        ],
                        stream=False
                    )
                    report = report_response.choices[0].message.content.strip()
                    print(f"[DEBUG] AI面试报告: {report[:100]}")
                    return jsonify({
                        'success': True,
                        'finished': True,
                        'report': report,
                        'question': history[-1]['q'] if history else '',
                        'analysis': analysis,
                        'next_idx': next_idx,
                        'max_questions': max_questions
                    })
                else:
                    # 最后一题加标注
                    is_last = (next_idx + 1) == max_questions
                    interview_prompt = f"根据以下简历内容，为该候选人生成第{next_idx+1}个有针对性的面试问题，突出意向岗位、技能或经验：\n{text}"
                    interview_response = client.chat.completions.create(
                        model="deepseek-chat",
                        messages=[
                            {"role": "system", "content": "你是一个专业的HR面试官，能根据简历内容生成有针对性的面试问题。"},
                            {"role": "user", "content": interview_prompt},
                        ],
                        stream=False
                    )
                    next_question = interview_response.choices[0].message.content.strip()
                    if is_last:
                        next_question = "【最后一题】" + next_question
                    print(f"[DEBUG] 下一题AI问题: {next_question}")
                    return jsonify({
                        'success': True,
                        'finished': False,
                        'question': history[-1]['q'] if history else '',
                        'analysis': analysis,
                        'next_idx': next_idx,
                        'next_question': next_question,
                        'max_questions': max_questions
                    })
            except Exception as e:
                print(f"[ERROR] AI分析/生成问题异常: {e}")
                return jsonify({'success': False, 'msg': f'AI分析/生成问题异常: {e}'})
    except Exception as e:
        print(f"[ERROR] interview_chat_api全局异常: {e}")
        return jsonify({'success': False, 'msg': f'全局异常: {e}'})
# ...existing code...
if __name__ == '__main__':
    print("[ROUTES] Flask已注册路由:")
    for rule in app.url_map.iter_rules():
        print(f"{rule} -> {rule.endpoint}")
    app.run(debug=True)